package graph_viewer.structure;

import java.io.*;
import java.util.*;

import graph_viewer.animator.*;
import graph_viewer.app_exception.*;
import graph_viewer.canvas.*;
import graph_viewer.layout.*;
import graph_viewer.structure.builder.*;
import graph_viewer.util.*;

/**
	adjust a layout to avoid cluttered nodes and different kinds of crossings.
*/
public class  Node_Distortor{	
	final public int DEMAG_DOI	= 1;
	Graph_Canvas graph_canvas;

	final	double ANIMATION_TIME_INTERVAL	= 0.1;

	public static void main(String arg[])  throws IOException{}
	//============================================================================
	// constructors
	//============================================================================
	public Node_Distortor( Graph_Canvas a_graph_canvas ){
		this.graph_canvas	= a_graph_canvas;
	}
	
	//============================================================================
	// public methods
	//============================================================================
	public void distort_leaf_clusters( Graph a_graph, Node focus_node, int thresholding, boolean is_animation ){
		if ( is_animation ){
			distort_leaf_clusters_animation ( a_graph, focus_node, thresholding );
		}else{
			distort_leaf_clusters_action ( a_graph, focus_node, thresholding );
		}
	}

	//============================================================================
	// public methods
	//============================================================================

	/**
		if a leaf cluster has edge to focus_node, keep its normal size; otherwise, demagnifiy it
	*/
	private void distort_leaf_clusters_action( Graph a_graph, Node focus_node, int thresholding ){
		ArrayList nodes_to_distort	= find_nodes_to_distort( a_graph, focus_node, thresholding );

		for ( Iterator _i = nodes_to_distort.iterator(); _i.hasNext(); ){
			Node a_node_to_distort	= (Node)_i.next();			
			int depth	= a_graph.find_tree().depth( a_node_to_distort );

			if ( depth > thresholding ){				
				distort_node( a_node_to_distort );
			}
		}//end of iterator
	}
	
	// show the distortion process. nodes are distorted form left to right
	private void distort_leaf_clusters_animation( Graph a_graph, Node focus_node, int thresholding ){					
		ArrayList nodes_to_distort	= find_nodes_to_distort( a_graph, focus_node, thresholding );
		
		
		Layout_Util.sort( nodes_to_distort,  Public_Values.X_AXIS);
		
		for ( Iterator _i = nodes_to_distort.iterator(); _i.hasNext(); ){
			Node a_node_to_distort	= (Node)_i.next();
			
			
			distort_node( a_node_to_distort );
			Animator_Util.clear_range( graph_canvas );
			Animator_Util.pause( ANIMATION_TIME_INTERVAL );
		}//end of iterator
		
		
		/*int height	= a_graph.find_tree().height();
		while ( height-- >= 0 ){
			ArrayList same_height_node_labels = a_graph.find_same_height_non_psuedo_nodes( height );
			
			if (! has_opened_node_label( a_graph, same_height_node_labels ) ){
				continue;
			}

			Layout_Util.sort_by_label( a_graph, same_height_node_labels,  Public_Values.X_AXIS);

			for ( Iterator _i = same_height_node_labels.iterator(); _i.hasNext(); ){
				String	node_label	= (String)_i.next();
				Node	a_cluster		= a_graph.search( node_label );
				if ( !a_cluster.get_is_closed() && a_cluster != focus_node ){
					distort_leaf_clusters_under_an_cluster_animation( a_cluster, a_graph, focus_node, thresholding );
				}				
			}
			
			
			Animator_Util.pause( ANIMATION_TIME_INTERVAL );
		}*/
	}
	
	private boolean has_opened_node( ArrayList nodes, Node focus ){
		boolean has_opened_node	= false;

		for ( Iterator _i = nodes.iterator(); _i.hasNext(); ){
			Node a_node	= (Node)_i.next();

			if ( !a_node.get_is_closed() && !a_node.get_label().equals( focus.get_label() ) ){
				has_opened_node = true;
			}				
		}

		return has_opened_node;
	}

	private boolean has_opened_node_label( Graph a_graph, ArrayList node_labels ){
		boolean has_opened_node	= false;

		for ( Iterator _i = node_labels.iterator(); _i.hasNext(); ){
			
			String	node_label	= (String)_i.next();
			Node	a_node		= a_graph.search( node_label );

			if ( !a_node.get_is_closed() ){
				has_opened_node = true;
			}				
		}

		return has_opened_node;
	}

	private void distort_leaf_clusters_under_an_cluster_animation( Node a_cluster, Graph a_graph, Node focus_node, int thresholding ){
		ArrayList children	= a_cluster.get_children();

		Layout_Util.sort( children,  Public_Values.X_AXIS);

		for ( Iterator _i = children.iterator(); _i.hasNext(); ){
			Node a_child	= (Node)_i.next();

			if ( a_child.is_leaf_cluster() && is_to_distort( a_child, a_graph, focus_node, thresholding ) ){
				Node_Position	 before_distortion_position	= new Node_Position( a_child.get_position() );
				
				distort_node( a_child );
				Animator_Util.clear_range( graph_canvas );
				Animator_Util.pause( ANIMATION_TIME_INTERVAL );
			}
		}
	}
	
	private void distort_node( Node a_node ){
		a_node.set_DOI( Graph.MIN_DOI );
		Node_Position_Helper.set_min_size( a_node );
	}
	
	private boolean is_to_distort( Node a_node, Graph a_graph, Node focus_node, int thresholding ){
		boolean
			is_connected_to_focus	= a_graph.is_connected( a_node, focus_node ),
			is_focus				= a_node.get_label().equals( focus_node.get_label() ),
			is_psuedo_node		= a_node.get_is_psuedo(),
			is_focus_sibling		= a_node.is_sibling( focus_node ),
			is_filtered				= a_graph.find_tree().depth( a_node ) > thresholding;
		
		boolean is_to_distort	= !( is_connected_to_focus || is_psuedo_node || is_focus_sibling || is_focus ) && is_filtered;

		/*
		Debug.println( a_node+" is_connected_to_focus: " + is_connected_to_focus, 4 );
		Debug.println( a_node+" is_psuedo_node: " + is_psuedo_node, 4 );
		Debug.println( a_node+" is_focus_sibling: " + is_focus_sibling, 4 );
		Debug.println( a_node+" is_filtered: " + is_filtered, 4 );
		
		Debug.println( a_node+" is_to_distort: " + is_to_distort, 4 );
		*/

		return is_to_distort;
	}
	
	public ArrayList find_nodes_to_distort( Graph a_graph, Node focus_node, int thresholding ){
		ArrayList nodes_to_distort = new ArrayList();

		ArrayList leaf_clusters	= a_graph.find_leaf_clusters(  );
		
		Debug.println( "\tfrom " + leaf_clusters + "  to  find nodes to be distorted when focused on " + focus_node, 24 );
		
		for ( Iterator _i = leaf_clusters.iterator(); _i.hasNext(); ){
			Node a_leaf_cluster	= (Node)_i.next();

			if ( is_to_distort ( a_leaf_cluster, a_graph, focus_node, thresholding )  ){
				Debug.println( a_leaf_cluster + " is to be distorted ", 24 );
				nodes_to_distort.add( a_leaf_cluster );
			}//end of if !a_graph.

		}//end of iterator

		return nodes_to_distort;
	}
	


}